package com.atguigu.spring.aop;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.*;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

import java.util.Arrays;

@Component
@Aspect //将当前组件表示为切面
@Order(2)
public class LoggerAspect {
    /*
    1.编写前置通知，使用@Before注解标识为前置通知方法
    前置方法在目标方法执行前执行
    注解的()里要使用切入点表达式，指明连接点
    @After后置通知，在finally语句中执行
    @AfterReturning返回通知，在return返回值之后执行
    @afterThrowing：异常通知，在目标方法的catch子句中执行
     */
    /*
    2.切入点表达式的位置：设置在通知注释的value属性中
    切入点表达式怎么写：
    "execution(希望切入方法的访问修饰符 希望切入方法的返回类型 希望切入方法的全类名.方法名(参数类型列表))
    可以使用*表示适配全部访问修饰符和全部访问类型，
    全类名.*表示适用类中全部方法，
    包.*或者某个包里的全部类
    (..)表示适配所有的参数列表
    切入点表达式的重用：使用注解Pointcut声明一个公共的切入点表达式方法,
    在需要使用的通知注解中，使用该方法名代替切入点表达式即可
     */
    /*
    3.在通知方法中设置JoinPoint参数，就可以获取连接点对应方法的各种信息
     */
    @Pointcut("execution(* com.atguigu.spring.aop.CalculatorImpl.*(..))")
    public void pointCut(){}

    @Before("pointCut()")
    public void beforeAdviceMethod(JoinPoint joinPoint){
        //joinPoint：连接点，通过这个对象，可以获取连接点的各种信息
        //获取连接点对应方法的签名（声明）信息
        Signature signature = joinPoint.getSignature();
        //获取连接点对应方法的参数
        Object[] args = joinPoint.getArgs();
        System.out.println("LoggerAspect:前置通知"+" 方法名:"+signature.getName()+" 参数信息："+ Arrays.toString(args));
    }

    @After("pointCut()")
    public void afterAdviceMethod(JoinPoint joinPoint){
        Signature signature = joinPoint.getSignature();
        Object[] args = joinPoint.getArgs();
        System.out.println("LoggerAspect:后置通知 "+"方法名："+signature.getName()+" 执行完毕");
    }

    /*在返回通知中如果想获得目标方法的返回值，需要注解@AfterReturning的returning属性
    就可以将通知方法的某参数(这里是result参数)指定为接受目标方法返回值的参数*/
    @AfterReturning(value = "pointCut()",returning="result")
    public void afterReturningAdviceMethod(JoinPoint joinPoint,Object result){
        Signature signature = joinPoint.getSignature();
        System.out.println("LoggerAspect：返回通知"+" 方法名："+signature.getName()+" 结果:"+result);

    }
    /*在异常通知中如果想获得目标方法的异常，需要注解@AfterThrowing的throwing属性
       就可以将通知方法的某参数(这里是exception参数)指定为接受目标方法异常的参数*/
    @AfterThrowing(value = "pointCut()",throwing = "exception")
    public void afterThrowingAdviceMethod(JoinPoint joinPoint,Throwable exception){
        Signature signature = joinPoint.getSignature();
        System.out.println("LoggerAspect：异常通知"+" 方法名："+signature.getName()+" 异常："+exception);

    }

    /*
    //使用ProceedingJoinPoint，其中有一个方法proceed表示执行目标对象的方法
    @Around("pointCut()")
    public Object aroundAdviceMethod(ProceedingJoinPoint proceedingJoinPoint){
        Signature signature = proceedingJoinPoint.getSignature();
        Object[] args = proceedingJoinPoint.getArgs();
        Object result = null;
        try{
            //前置通知
            System.out.println("LoggerAspect:环绕通知->前置通知"+" 方法名:"+signature.getName()+" 参数信息："+ Arrays.toString(args));
            //表示目标对象方法的执行
            result = proceedingJoinPoint.proceed();
            System.out.println("LoggerAspect:环绕通知->返回通知"+" 方法名："+signature.getName()+" 结果:"+result);

        }catch (Throwable throwable){
            throwable.printStackTrace();
            System.out.println("LoggerAspect：环绕通知->异常通知"+" 方法名："+signature.getName()+" 异常："+throwable);

        }finally {
            //后置通知
            System.out.println("LoggerAspect:环绕通知->后置通知 "+"方法名："+signature.getName()+" 执行完毕");

        }
        return result;
    }

*/
}
